from __future__ import absolute_import
from __future__ import print_function

import time
from collections import defaultdict
from multiprocessing import Process, Queue

import sumolib
import traci.constants as tc
from loguru import logger

from config.base_config import GLOBAL_POLYGON_CENTER, MAX_SPEED, USE_LIBSUMO, USE_GUI, KAFKA_E1_TOPIC
from utils import sumo_utils

if USE_LIBSUMO:
    import libsumo as traci
else:
    import traci

logger.add("debug.log", format="{time} {level} {message}", level="DEBUG")


class SumoMain:
    """
        sumo 主类，定义sumo多种处理逻辑
    """

    def __init__(self, sumo_net_file, sumo_cfg_file, gui, process_id):
        """
                仿真初始化
                :param sumo_net_file:  仿真路网文件路径
                :param sumo_cfg_file:  仿真配置文件路径
                :param gui: 是否开启gui
                """
        # ---------------------------- 路网相关配置 -----------------------------

        self.sumo_net_file = sumo_net_file
        self.sumo_cfg_file = sumo_cfg_file
        self.gui = gui
        self.sumo_net_instance = None
        # 基站路由配置
        self.route_basestation = dict()
        # ############################### 轨迹数据 ##############################
        # 所有在网仿真车辆
        self.global_vehicles = None
        # 发车信息表
        self.sn_send_car_info_dict = defaultdict(dict)
        # ################################ 其他配置 #############################
        self.process_id = process_id
        self.global_num = 0
        self.global_time = time.time()
        self.init_sumo()

    def kafka_data_listen(self):
        pass

    def senf_kafka_E1(self, q_s, p_id):
        pass

    def init_sumo(self):
        """
        初始化sumo  加载
        :return:
        """
        # 加载路网
        self.sumo_net_instance = sumolib.net.readNet(self.sumo_net_file, withInternal=True, withLatestPrograms=True)
        # 启动仿真
        sumo_utils.start_sumo(self.sumo_cfg_file, False, gui=self.gui)
        # 设置所有车道的最大通行速度（150km/h）
        for edge_id in traci.lane.getIDList():
            traci.lane.setMaxSpeed(edge_id, MAX_SPEED)
        # 新增区域中心矩形，用于订阅100KM范围内的所有仿真车辆信息，并配置监听信息内容
        traci.polygon.add('GLOBAL_POLYGON_CENTER', GLOBAL_POLYGON_CENTER, (1, 0, 1))
        traci.polygon.subscribeContext('GLOBAL_POLYGON_CENTER',
                                       tc.CMD_GET_VEHICLE_VARIABLE, 1000000,
                                       [tc.VAR_SPEED, tc.VAR_TYPE, tc.VAR_POSITION, tc.VAR_ANGLE, tc.VAR_ACCEL])

    def main_loop(self):

        # 仿真结果输出队列
        queue_data = Queue(maxsize=2000)  # 多进程队列在此处创建
        Process(target=self.senf_kafka_E1, args=(queue_data, self.process_id,)).start()
        # 在子进程中初始化多进程队列和资源
        # 获取雷达感知数据队列
        re_queue_data = Queue(maxsize=2000)  # 多进程队列在此处创建
        son_process = Process(target=self.kafka_data_listen,
                              args=(KAFKA_E1_TOPIC, re_queue_data, queue_data, process_id), daemon=True)
        son_process.start()
        # 车辆记录
        all_set = set()
        low_frame_num = 0
        batch_num = 0
        while True:
            start_time = time.time()
            # 获取当前仿真在网车辆
            self.global_vehicles = traci.polygon.getContextSubscriptionResults('GLOBAL_POLYGON_CENTER')
            # 车辆发车控制
            self.control_send_car()
            # 车辆行为控制
            self.control_lidar_car()
            # 删车控制
            self.control_delete_car()
            batch_num += 1
            end_time = time.time()
            # 进程保活控制
            if end_time - self.global_time > 1:
                time_data.put(int(time.time()))
                simu_frame = abs(self.global_num - batch_num)
                logger.info(f"进程{self.process_id} 仿真帧率 {simu_frame} 仿真数量{len(self.global_vehicles)}")
                if simu_frame < 7:
                    low_frame_num += 1
                if low_frame_num > 10 or (self.global_num > 2000 and len(self.global_vehicles) == 0):
                    logger.info(f"关闭子进程")
                    son_process.terminate()
                    son_process.join()
                    stop_data.put(1)
                self.global_time = end_time
                self.global_num = batch_num
            traci.simulationStep()

    def control_send_car(self):
        """车辆发车控制"""
        pass

    def control_lidar_car(self):
        """车辆发车控制"""
        pass

    def control_delete_car(self):
        """删车控制"""
        pass


def run_sumo_simulation(sumo_net_file, sumo_cfg_file, gui, process_id, stop_data, time_data):
    sumo_main = SumoMain(sumo_net_file, sumo_cfg_file, gui, process_id)
    sumo_main.main_loop(stop_data, time_data)


if __name__ == '__main__':
    # 路网文件
    sumo_net_file = "./net/henan.net.xml"
    # 配置文件
    sumo_cfg_file = "./net/henan.sumocfg"
    # 是否启用gui可视化界面，调试阶段启用，线上部署关闭减少资源占用
    stop_data = Queue(maxsize=2000)  # 创建队列
    time_data = Queue(maxsize=2000)  # 创建队列
    gui = USE_GUI
    process_id = 1
    logger.info("开启进程")
    tess_process = Process(target=run_sumo_simulation,
                           args=(sumo_net_file, sumo_cfg_file, gui, process_id, stop_data, time_data))
    tess_process.start()

    time_simu = int(time.time())
    while True:
        stop_flag = False
        while time_data.qsize() > 0:
            time_simu = time_data.get()
        if time_simu:
            logger.info(f"时间差{abs(time_simu - int(time.time()))}")
            if abs(time_simu - int(time.time())) > 60:
                stop_flag = True
                time_simu = int(time.time())
        while stop_data.qsize() > 0:
            _ = stop_data.get()
            stop_flag = True
        if stop_flag:
            logger.info("关闭开启线程")
            tess_process.terminate()
            tess_process.join()
            logger.info("重新开启线程")
            tess_process = Process(target=run_sumo_simulation,
                                   args=(sumo_net_file, sumo_cfg_file, gui, process_id, stop_data, time_data))
            tess_process.start()
        time.sleep(1)
